package main;

import com.item.manager.repository.TemplateRepository;
import encounter.Encounter;
import item.Item;
import item.ItemProvider;
import location.Location;
import location.ToLocationFunction;
import serialize.LocationLoader;

import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;

import static java.util.stream.Collectors.toList;

public class Village implements Location {
    private final String name;

    private final transient List<Location> exits = new ArrayList<>();


    public Village(TemplateRepository monsterRepo, TemplateRepository itemRepo,
                   TemplateRepository trapRepo, TemplateRepository lootRepo) {
        LocationLoader locationLoader = new LocationLoader("simple-scenario.yml");
        ToLocationFunction function = new ToLocationFunction(monsterRepo, trapRepo, lootRepo, new ItemProvider(itemRepo));

        this.name = "Village";

        List<Location> locations = locationLoader.get().stream().map(function).collect(toList());
        locations.forEach(l -> l.getExits().add(this));
        exits.addAll(locations);
    }

    @Override
    public boolean isVillage() {
        return true;
    }

    @Override
    public List<Location> getExits() {
        return this.exits;
    }

    @Override
    public String getName() {
        return this.name;
    }

    @Override
    public void visit(Hero hero) {
        if (hero.getRescue().isPresent()) {
            rescue(hero);
        }

        if (!hero.getTrohpies().isEmpty()) {
            giveTrophies(hero);
        } else if (!hero.getItems().isEmpty()) {
            sellItems(hero);
        } else if (hero.getEnergy() == 1.0 && hero.getHealth() == hero.getMaxHealth()) {
            travel(hero);
        } else {
            rest(hero);
        }
    }

    private void rescue(Hero hero) {
        Hero fallenHero = hero.getRescue().get();
        hero.clearRescue();
        Logger.log(hero + " saves " + fallenHero + ".");
        hero.addEvent("Saves " + fallenHero);
        fallenHero.addEvent("Is saved by " + hero);
        if (!fallenHero.isDeceased()) {
            fallenHero.updateHealth(0.1);
            fallenHero.updateEnergy(-1.0);
        }
    }

    private void giveTrophies(Hero hero) {
        for (Iterator<Trophy> it = hero.getTrohpies().iterator(); it.hasNext();) {
            Trophy next = it.next();
            Logger.log(hero + " gives " + next + " for 50 copper.");
            hero.updateCopper(50);
            it.remove();
        }
    }

    private void sellItems(Hero hero) {
        for (Item item : new HashSet<>(hero.getItems().keySet())) {
            int count = hero.getItems().get(item);
            int totalCost = item.getCost() * count;
            if (totalCost > 0) {
                Logger.log(hero + " sells " + count + " " + item + " for " + totalCost + ".");
                hero.updateCopper(totalCost);
                hero.getItems().remove(item);
            }
        }
    }

    private void rest(Hero hero) {
        Logger.log(hero + " rests.");

        if (hero.getCopper() >= 25) {
            hero.updateHealth(0.5);
            hero.updateEnergy(0.2);
            hero.updateCopper(-25);
        } else if (hero.getCopper() >= 5) {
            hero.updateHealth(0.25);
            hero.updateEnergy(0.1);
            hero.updateCopper(-5);
        } else {
            hero.updateHealth(0.1);
            hero.updateEnergy(0.01);
        }
    }

    private void travel(Hero hero) {
        Logger.log(hero + " starts adventure.");
        Location exit = RandomProvider.chooseRandom(this.getExits());
        hero.addEvent("Goes to " + exit + ".");
        hero.moveTo(exit);
        hero.updateEnergy(-0.01);
    }

    @Override
    public void process() {
        // nothing so far
    }

    @Override
    public List<Item> getItems() {
        return null;
    }

    @Override
    public String toString() {
        return "Village";
    }

    @Override
    public void addEncounter(Encounter encounter, int weight) {
        throw new RuntimeException("Not implemented!");
    }
}
